﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.ComponentModel;

using Optimization.Utilities;

namespace Optimization.StandardSolvers
{
    [IsSolver]
    public class CFO : ParticleBasedMethod, ISolver
    {
        class ParticleCFO : Particle
        {
            public Vector Acceleration;
        }


        //http://sci2s.ugr.es/EAMHCO/pdf/CFO.pdf

        public void DefineProblem(Func<IProblem> cf)
        {

            ProblemFactory = cf;
            Problem = ProblemFactory();

            if (Problem == null)
                throw new Exception("ClassFactory for the problem did not create instance with IProblem interface");

            Solution = Problem.RandomSolution();
            //CFO is Maximizing
            SolutionValue = -Problem.Evaluate(Solution);

            InitSwarm();
        }

        int swarmSize = 20;
        public void InitSwarm()
        {
            for (int i = 0; i < swarmSize; i++)
            {
                ParticleCFO p = new ParticleCFO();
                p.Position = Problem.RandomSolution();
                p.Velocity = Vector.Zero(p.Position.Length);
                p.Acceleration = Vector.Zero(p.Position.Length);
                p.Value = Problem.Evaluate(p.Position);

                this.Add(p);
            }
        }

        private void Evaluate()
        {
            foreach (ParticleCFO s in this)
            {
                //CFO is Maximizing
                s.Value = -Problem.Evaluate(s.Position);

                //CFO is Maximizing
                if (SolutionValue < s.Value)
                {
                    SolutionValue = s.Value;
                    Solution = s.Position.Copy();
                }
            }
        }

        private void CalculateAcceleration()
        {
            foreach (ParticleCFO aParticle in this)
            {
                //set to zero
                aParticle.Acceleration = Vector.Zero(aParticle.Acceleration.Length);

                foreach (ParticleCFO bParticle in this)
                {
                    if (aParticle == bParticle)
                        continue;

                    Vector orientation = bParticle.Position - aParticle.Position;
                    double orientationSize = orientation.Abs();

                    //CFO is Maximizing
                    double massDifference = (bParticle.Value - aParticle.Value);
                    if (massDifference <= 0)
                        //U() function is zero
                        continue;

                    double massDifferencePow = Math.Pow(massDifference, alfa);
                    double orientationSizePow = Math.Pow(orientationSize, beta);

                    Vector a2bAcceleration = massDifferencePow / orientationSizePow * orientation;

                    aParticle.Acceleration = aParticle.Acceleration + a2bAcceleration;
                }

                aParticle.Acceleration = G * aParticle.Acceleration;
            }
        }

        private void CalculateNewPositions()
        {
            double StepSize = 1;
            foreach (ParticleCFO s in this)
            {
                //CFO doesnt use velocity so this is just storing
                //s.Velocity = 0.5 * StepSize * s.Acceleration;
                //s.Position = s.Position + StepSize * s.Velocity;

                //in fact the calculation have to be
                s.Position = s.Position + 0.5 * StepSize * StepSize * s.Acceleration;
                //which is same as above
            }
        }

        private double g = 15;
        [CategoryAttribute("CFO Settings"), DescriptionAttribute("G")]
        public double G
        {
            get { return g; }
            set { g = value; }
        }

        private double alfa = 2;
        [CategoryAttribute("CFO Settings"), DescriptionAttribute("Alfa")]
        public double Alfa
        {
            get { return alfa; }
            set { alfa = value; }
        }

        private double beta = 2;
        [CategoryAttribute("CFO Settings"), DescriptionAttribute("Beta")]
        public double Beta
        {
            get { return beta; }
            set { beta = value; }
        }



        public Vector GetSolution()
        {
            return Solution.Copy();
        }

        public void SetSolution(Vector solution, double solutionValue)
        {
            throw new NotImplementedException();
        }

        public double GetSolutionValue()
        {
            //CFO is Maximizing
            return -SolutionValue;
        }

        public void Step()
        {
            CalculateAcceleration();
            CalculateNewPositions();
            Evaluate();
        }
    }
}
